﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.GamerServices;
using System.IO;
using System.Xml.Serialization;
using Tomahawk.Serialization;
using Tomahawk.Serialization.SerialBoost;


namespace Tomahawk.Runtime.Storage
{

    public class StorageManager
    {
        /*
        /// <summary>
        /// The Device Manager component (profile related)
        /// </summary>
        private StorageDeviceManager playerStorageDeviceManager;
        */
        /// <summary>
        /// The Device Manager used to store content (non profile related)
        /// </summary>
        private StorageDeviceManager gameStorageDeviceManager;

        /// <summary>
        /// The game instance
        /// </summary>
        private Game game = null;

        /// <summary>
        /// Initialization flag
        /// </summary>
        private bool initialized = false;

        #region "Public Properties"

        /// <summary>
        /// Save/Load from Storage device is disabled
        /// </summary>
        public bool? SaveDisabled { get; set; }

        /*
        /// <summary>
        /// Indicate that there is a device selected for storage (player profile dependant)
        /// </summary>
        private bool PlayerDeviceSelected { get; set; }
        */

        /// <summary>
        /// Indicate that there is a device selected for storage (player agnostic)
        /// </summary>
        private bool GameDeviceSelected { get; set; }

        /// <summary>
        /// Indicates when all devices are selected, and storage therefore ready to be used
        /// </summary>
        public bool AllDevicesSelected
        {
            get
            {
                return (/*this.PlayerDeviceSelected &&*/ this.GameDeviceSelected);
            }
        }

        /// <summary>
        /// Storage is ready for being used
        /// </summary>
        public bool ReadyToOperate
        {
            get
            {
                //The device is considered ready to store content
                //when either the all storage devices had been selected
                //or when SaveDisabled had been set to TRUE
                return (
                            this.AllDevicesSelected
                            ||
                            (this.SaveDisabled.HasValue && this.SaveDisabled.Value)
                       );
            }
        }

        #endregion

        #region "Initialization"

        /// <summary>
        /// Constructor
        /// </summary>
        public StorageManager(Game game)
        {
            this.game = game;
            this.initialized = false;
            //this.SaveDisabled = null;
            //this.PlayerDeviceSelected = false;
            this.GameDeviceSelected = false;
        }

        /// <summary>
        /// Initialization
        /// </summary>
        public void Initialize()
        {
            if (!initialized)
            {
                /*
                //
                // Player (profile related) storage
                //

                //if we are signed with a profile, initialize the storage manager against the profile
                if (Engine.Instance.PlayerManager.DefaultPlayer.Gamer != null)
                {
                    //instance the storage device manager against the signed player profile
                    PlayerIndex index = Engine.Instance.PlayerManager.DefaultPlayer.GamepadIndex;
                    this.playerStorageDeviceManager = new StorageDeviceManager(game, index);
                }
                else
                {
                    //if not, use the common storage location (for all profiles)
                    this.playerStorageDeviceManager = new StorageDeviceManager(game);
                }

                //add the storage manager component 
                game.Components.Add(this.playerStorageDeviceManager);

                //subscribe to events
                this.playerStorageDeviceManager.DeviceDisconnected += new EventHandler<StorageDeviceEventArgs>(storageDeviceManager_DeviceDisconnected);
                this.playerStorageDeviceManager.DevicePromptClosed += new EventHandler<StorageDevicePromptEventArgs>(storageDeviceManager_DevicePromptClosed);
                this.playerStorageDeviceManager.DeviceSelected += new EventHandler(storageDeviceManager_DeviceSelected);
                this.playerStorageDeviceManager.DeviceSelectorCanceled += new EventHandler<StorageDeviceEventArgs>(storageDeviceManager_DeviceSelectorCanceled);
                */

                //
                // Game (non profile related) storage
                //
                
                //create a new storage device
                this.gameStorageDeviceManager = new StorageDeviceManager(game);

                //add the storage manager component 
                game.Components.Add(this.gameStorageDeviceManager);

                //subscribe to events
                this.gameStorageDeviceManager.DeviceDisconnected += new EventHandler<StorageDeviceEventArgs>(gameStorageDeviceManager_DeviceDisconnected);
                this.gameStorageDeviceManager.DevicePromptClosed += new EventHandler<StorageDevicePromptEventArgs>(gameStorageDeviceManager_DevicePromptClosed);
                this.gameStorageDeviceManager.DeviceSelected += new EventHandler(gameStorageDeviceManager_DeviceSelected);
                this.gameStorageDeviceManager.DeviceSelectorCanceled += new EventHandler<StorageDeviceEventArgs>(gameStorageDeviceManager_DeviceSelectorCanceled);

                //init flag
                this.initialized = true;
            }

        }

        #endregion

        #region "Device Manager events handlers"

        void gameStorageDeviceManager_DeviceSelectorCanceled(object sender, StorageDeviceEventArgs e)
        {
            this.GameDeviceSelected = false;
            e.EventResponse = StorageDeviceSelectorEventResponse.Force;
        }

        void gameStorageDeviceManager_DeviceDisconnected(object sender, StorageDeviceEventArgs e)
        {
            this.GameDeviceSelected = false;
            e.EventResponse = StorageDeviceSelectorEventResponse.Force;
        }

        void gameStorageDeviceManager_DeviceSelected(object sender, EventArgs e)
        {
            this.GameDeviceSelected = true;
        }

        void gameStorageDeviceManager_DevicePromptClosed(object sender, StorageDevicePromptEventArgs e)
        {
        }

        /*
        void storageDeviceManager_DeviceSelectorCanceled(object sender, StorageDeviceEventArgs e)
        {
            this.PlayerDeviceSelected = false;
            e.EventResponse = StorageDeviceSelectorEventResponse.Force;
        }
        void storageDeviceManager_DeviceDisconnected(object sender, StorageDeviceEventArgs e)
        {
            this.PlayerDeviceSelected = false;
            e.EventResponse = StorageDeviceSelectorEventResponse.Force;
        }
        void storageDeviceManager_DeviceSelected(object sender, EventArgs e)
        {
            this.PlayerDeviceSelected = true;
        }
        void storageDeviceManager_DevicePromptClosed(object sender, StorageDevicePromptEventArgs e)
        {
        }
        */

        #endregion

        /// <summary>
        /// Prompt user for device
        /// </summary>
        public void PromptForDevice()
        {
            promptingForDevices = true;
            //playerDevicePrompted = false;
            gameDevicePrompted = false;
        }

        /*
        /// <summary>
        /// Available storage devices
        /// </summary>
        public enum StorageDevices
        {
            PlayerDevice,
            GameDevice
        }
        */

        /// <summary>
        /// Saves an ISerializable object to a storage device
        /// </summary>
        public void Save<T>(/*StorageDevices device,*/ T saveObject, string folderName, string destinationFileName) where T : ISerializable
        {
            //get the propper device
            /*StorageDeviceManager deviceManager = null;
            if (device == StorageDevices.GameDevice)
            {
                deviceManager = this.gameStorageDeviceManager;
            }
            else
            {
                deviceManager = this.playerStorageDeviceManager;
            }*/

            StorageDeviceManager deviceManager = this.gameStorageDeviceManager;

            //save the object
            if ((deviceManager != null) && (deviceManager.Device != null) && (this.SaveDisabled.HasValue && !this.SaveDisabled.Value))
            {
                //open the appropiate container
                StorageContainer container = deviceManager.Device.OpenContainer(folderName);

                //open the write stream
                string filename = Path.Combine(container.Path, destinationFileName);
                FileStream stream = File.Open(filename, FileMode.Create, FileAccess.Write);

                //save the object to file
                saveObject.WriteToFile(stream);

                //Debug info:
                Engine.Instance.DebugConsole.Warn("Object succesfully save to file: " + filename);

                //close stream and container
                stream.Close();
                container.Dispose();
            }
        }

        /// <summary>
        /// Save an object to the container
        /// </summary>
        public void SaveObject(/*StorageDevices device,*/ object saveObject, string folderName, string destinationFileName)
        {
            //get the propper device
            /*StorageDeviceManager deviceManager = null;
            if (device == StorageDevices.GameDevice)
            {
                deviceManager = this.gameStorageDeviceManager;
            }
            else
            {
                deviceManager = this.playerStorageDeviceManager;
            }*/

            StorageDeviceManager deviceManager = this.gameStorageDeviceManager;

            //save the object
            if ((deviceManager != null) && (deviceManager.Device != null) && (this.SaveDisabled.HasValue && !this.SaveDisabled.Value))
            {
                //open the appropiate container
                StorageContainer container = deviceManager.Device.OpenContainer(folderName);

                //open the write stream
                string filename = Path.Combine(container.Path, destinationFileName);
                FileStream stream = File.Open(filename, FileMode.Create, FileAccess.Write);

                //Convert object to XML 
                //XmlSerializer serializer = SerializationPool.Instance.GetXmlSerializer(saveObject.GetType());
                //serializer.Serialize(stream, saveObject);

                MemoryStream ms = SerialBoost.Instance.Serialize(saveObject);
                stream.Write(ms.GetBuffer(), 0, (int)ms.Length);

                //Debug info:
                Engine.Instance.DebugConsole.Warn("Object succesfully save to file: " + filename);

                //close stream and container
                ms.Close();
                stream.Close();
                container.Dispose();
            }
        }

        /// <summary>
        /// Loads an ISerializable object from storage
        /// </summary>
        public void Load<T>(/*StorageDevices device,*/ ref T loadObject, string folderName, string sourceFileName) where T : ISerializable
        {
            //get the propper device
            /*StorageDeviceManager deviceManager = null;
            if (device == StorageDevices.GameDevice)
            {
                deviceManager = this.gameStorageDeviceManager;
            }
            else
            {
                deviceManager = this.playerStorageDeviceManager;
            }*/

            StorageDeviceManager deviceManager = this.gameStorageDeviceManager;

            //load the object
            if ((deviceManager != null) && (deviceManager.Device != null) && (this.SaveDisabled.HasValue && !this.SaveDisabled.Value))
            {
                //open the appropiate container
                StorageContainer container = deviceManager.Device.OpenContainer(folderName);

                //open the write stream
                string filename = Path.Combine(container.Path, sourceFileName);
                if (File.Exists(filename))
                {
                    FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read);

                    loadObject.ReadFromFile(stream);

                    //Debug info:
                    Engine.Instance.DebugConsole.Warn("Object succesfully loaded from file: " + filename);

                    //close stream and container
                    stream.Close();
                }
                container.Dispose();
            }
        }

        /// <summary>
        /// Load an object from storage
        /// </summary>
        public object LoadObject(/*StorageDevices device,*/ Type objectType, string folderName, string sourceFileName)
        {
            object retVal = null;

            //get the propper device
            /*StorageDeviceManager deviceManager = null;
            if (device == StorageDevices.GameDevice)
            {
                deviceManager = this.gameStorageDeviceManager;
            }
            else
            {
                deviceManager = this.playerStorageDeviceManager;
            }*/

            StorageDeviceManager deviceManager = this.gameStorageDeviceManager;

            //load the object
            if ((deviceManager != null) && (deviceManager.Device != null) && (this.SaveDisabled.HasValue && !this.SaveDisabled.Value))
            {
                //open the appropiate container
                StorageContainer container = deviceManager.Device.OpenContainer(folderName);

                //open the write stream
                string filename = Path.Combine(container.Path, sourceFileName);
                if (File.Exists(filename))
                {
                    /*FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read);

                    // Convert XML into an object
                    XmlSerializer serializer = SerializationPool.Instance.GetXmlSerializer(objectType);
                    retVal = serializer.Deserialize(stream);*/

                    retVal = SerialBoost.Instance.Deserialize(objectType, filename);

                    //Debug info:
                    Engine.Instance.DebugConsole.Warn("Object succesfully loaded from file: " + filename);

                    /*//close stream and container
                    stream.Close();*/
                }
                container.Dispose();
            }

            return retVal;
        }

        /// <summary>
        /// Delete a file from the storage
        /// </summary>
        public void DeleteFile(/*StorageDevices device,*/ string folderName, string sourceFileName)
        {   
            //get the propper device
            /*StorageDeviceManager deviceManager = null;
            if (device == StorageDevices.GameDevice)
            {
                deviceManager = this.gameStorageDeviceManager;
            }
            else
            {
                deviceManager = this.playerStorageDeviceManager;
            }*/

            StorageDeviceManager deviceManager = this.gameStorageDeviceManager;

            if ((deviceManager != null) && (deviceManager.Device != null) && (this.SaveDisabled.HasValue && !this.SaveDisabled.Value))
            {
                //open the appropiate container
                StorageContainer container = deviceManager.Device.OpenContainer(folderName);

                //check for the file
                string filename = Path.Combine(container.Path, sourceFileName);
                if (File.Exists(filename)) File.Delete(filename);

                //Debug info:
                Engine.Instance.DebugConsole.Warn("File succesfully deleted: " + filename);

                //
                container.Dispose();
            }
        }

        /// <summary>
        /// Flag that indicates that we are prompting for devices
        /// </summary>
        private bool promptingForDevices = false;
        
        /*
        /// <summary>
        /// Flag that indicates if the player device had been prompted 
        /// </summary>
        private bool playerDevicePrompted = false;
        */

        /// <summary>
        /// Flag that indicates if the game device had been prompted 
        /// </summary>
        private bool gameDevicePrompted = false;

        /// <summary>
        /// Update the storage manager. Takes care of initializing both game and player devices
        /// </summary>
        public void Update()
        {
            if (promptingForDevices)
            {
                /*if (!this.PlayerDeviceSelected)
                {
                    if (!playerDevicePrompted)
                    {
                        playerDevicePrompted = true;
                        this.playerStorageDeviceManager.PromptForDevice();
                    }
                }
                else*/
                if (!this.GameDeviceSelected)
                {
                    if (!gameDevicePrompted)
                    {
                        gameDevicePrompted = true;
                        this.gameStorageDeviceManager.PromptForDevice();
                    }
                }
                else
                {
                    //done with prompting for devices
                    this.promptingForDevices = false;
                }
            }
        }

    }

}
